package jp.lab1;
import java.io.FileInputStream;
import java.io.FileOutputStream;

import java.io.IOException;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import java.util.Random;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;


import org.cybergarage.upnp.Action;
import org.cybergarage.upnp.ArgumentList;
import org.cybergarage.upnp.ControlPoint;
import org.cybergarage.upnp.Device;

import org.cybergarage.upnp.UPnP;
import org.cybergarage.upnp.device.NotifyListener;
import org.cybergarage.upnp.device.SearchResponseListener;
import org.cybergarage.upnp.event.EventListener;
import org.cybergarage.upnp.ssdp.SSDPPacket;
import org.cybergarage.util.Debug;

import org.geed.testbed.sub1.FooPacket;
import org.geed.testbed.sub1.FooUnicastServer;
import org.geed.testbed.sub1.FooBroadcastServer;
import org.geed.testbed.sub1.FooRequestListener;
import org.geed.testbed.sub1.FooUnicastListener;

import org.geed.testbed.sub1.FooResponse;

import org.geed.testbed.sub1.FooUSocket;


public class SecureControlPoint extends ControlPoint
	implements NotifyListener, EventListener, SearchResponseListener
	 			,FooRequestListener, FooUnicastListener{

	private final static String AIRCON_DEVICE_TYPE = "urn:schemas-upnp-org:device:secureaircon:1";
	private final static String AIRCON_SERVICE_TYPE = "urn:schemas-upnp-org:service:securegroup:1";
	
	// secure group connection state variable
	private final static String UNREGISTER = "STATE:UNREGISTER";
	private final static String REQUESTING = "STATE:REGISTER_REQUEST";
	private final static String CONFIRMING = "STATE:REGISTER_CONFIRM";
	private final static String REGISTERED = "STATE:REGISTERED";

	private String secureGroupStatus = UNREGISTER;
	
	//Secure group communication variable
	private int keylen=16;
	private int l=128; //ramda
	private byte[][] publicIDBytes;
	private byte[][] secretBytes;
	private byte[] secret;
	private byte[] groupKey;
	private String groupkeyID;
	
	//network parameters
	private int nodeConfirmTimeout = 5000; //3 seconds
	
	//debuging
	private boolean enableLogFile = false;
	
	
	private FooUnicastServer groupUSock = null;
	private FooBroadcastServer groupMuSock = null;
	
	private String ID;
	private int currentSeq;
	private String currentRequestStr;
	private String currentConfirmStr;
	
	private FileOutputStream out = null;

	
	public String getName(){
		return this.ID;
	}
	
	public String getStatus(){
		return this.secureGroupStatus;
	}
	
	public String getRequestStr(){
		return this.currentRequestStr;
	}
	public String getConfirmStr(){
		return this.currentConfirmStr;
	}
	
	public String getEncodedPublicID(){
		byte[] tmpPubID = new byte[(l+1)*keylen];
    	int current = 0;
    	for(int i=0; i<l+1; i++){
    		System.arraycopy(this.publicIDBytes[i], 0, tmpPubID, current, keylen);
    		current += keylen;
    	}
    	return  SecureControlPoint.toHexString(tmpPubID);
	}
	
	public boolean isRegistered(){
		if(this.secureGroupStatus.equals(UNREGISTER)){
			return false;
		}
		
		if(this.secureGroupStatus.equals(REGISTERED)){
			return true;
		}
		
		return false;
	}
	
	
	
	public SecureControlPoint(String name){
		this.ID = name;
		//Use only ipv4 address
		UPnP.setEnable(UPnP.USE_ONLY_IPV4_ADDR);
		
		addNotifyListener(this);
		addSearchResponseListener(this);
		addEventListener(this);
		
		Debug.off();
		
		//securegroup unisock (musock start later)
		//leave rekey message
		this.groupUSock = new FooUnicastServer();
		this.groupUSock.setPort(7000);
		this.groupUSock.setUnicastListener(this);
		this.groupUSock.start();
		
		if(this.enableLogFile){
			try{
				this.out = new FileOutputStream("log\\m"+getName()+".txt",false);
			}catch(Exception e){
				e.printStackTrace();
			}
		}
		
	}
/*	
	public boolean start(){
		
		Debug.off();
		return super.start();
	}
	
	public boolean stop(){
		//stopSecureGroup();
		return super.stop();
	}
	*/
	
	private void startSecureGroup(){

		
		
		//join rekey message
		groupMuSock = new FooBroadcastServer();
		groupMuSock.setFooRequestListener(this);
		groupMuSock.start();

		//System.out.println("Secure group communication: started");
		
		
	}
	
	/*
	private void stopSecureGroup(){
		respSock.close();
	}
	
	public FooUSocket getFooUSocket(){
		return (FooUSocket)this.respSock;
	}
	
	public int getPort(){
		return this.respSock.getPort();
	}
	*/
	
	//muticast 4466
	// join rekey listener
	public void performRegisterRequest(FooPacket pkg){
		byte[] dataBytes = pkg.getData();
		String dataStr = new String(dataBytes, 0, dataBytes.length);
		
		
		//System.out.println("[GROUP MESSAGE]on id="+this.ID/*+" msg="+dataStr*/);
		
		String[] dataArr = dataStr.split(":");
		String head = dataArr[0];
		
		if(head.equals("REKEY NOTIFY")){
			onRekeyNotify(pkg);
		}
			
	}
	
	/*
	 * When group member changed
	 * Group controller will notify to existing member
	 */
	private void onRekeyNotify(FooPacket pkg){
		//compute xor
		//update group key
		
		
		String ipAddr = pkg.getDatagramPacket().getAddress().getHostAddress();
		byte[] dataBytes = pkg.getData();
		String dataStr = new String(dataBytes, 0, dataBytes.length);
		String[] dataArr = dataStr.split(":");
		String body = dataArr[1];
		String[] bodyArr = body.split(",");
		
		String seq = bodyArr[0];
		String xorStr = bodyArr[1];
		String groupkeyID = bodyArr[2];
		String newnodeID = bodyArr[3];
		
		
		
		if(getGroupkeyID().equals(groupkeyID)){
			return;
		}
		
		byte[] xorBytes = hexStringToBytes(xorStr);
		
		byte[] newGroupKey = AirconDevice.bitwiseXOR(this.groupKey, xorBytes);
		setGroupKey(newGroupKey);
		setGroupkeyID(groupkeyID);
		
		//reply to server
		String requestStr = "REKEY CONFIRM:";
		requestStr += seq+",";
		requestStr += this.ID+",";
		requestStr += getGroupkeyID();
		
		FooResponse replyResponse = new FooResponse();
    	replyResponse.setData(requestStr.getBytes());

    	
    	FooUSocket uSock = null;
    	pkg = null;
    	
    	//random wait time to confirm
    	Random rand = new Random();
    	int waitTime = rand.nextInt(10000);
    	try{
    		Thread.sleep(waitTime);
    	}catch(Exception e){}
    	
    	while(pkg == null){
    		uSock = new FooUSocket();
    		uSock.open();
	    	uSock.post(ipAddr, 4556, replyResponse);
	    	//uSocket.close();
	    	
	    	//Now let's turn to device
	    	
	    	//4. Reg done
	    	//uSock = new FooUSocket();
			//uSock.open(remotePort);
			pkg = uSock.receive();
			uSock.close();
			
			if(pkg == null){
				//System.out.println("**** **** **** Rekey confirm timeout id="+this.ID);
				/*
				if(this.enableLogFile){
					String log = "**** **** **** Rekey confirm timeout id="+this.ID+"\n";
					try{
						this.out.write(log.getBytes());
					}catch(Exception e){}
				}*/
			}
			
    	}
    	
    	//System.out.println("Rekey success id="+ID+ " groupkeyID="+getGroupkeyID()+" newnode id="+newnodeID);
		
    	if(this.enableLogFile){
	    	String log = "Rekey success id="+ID+ " groupkeyID="+getGroupkeyID()+" newnode id="+newnodeID+"\n";
	    	try{
				this.out.write(log.getBytes());
			}catch(Exception e){}
    	}
    	
    	
	}
	
	
	//unicast dynamic port
	//use for leave rekey listener
	public void performFooResponse(FooPacket pkg){
		/*
		System.out.println("FooResponse Performed");
		
		byte[] databytes = pkg.getData();
		
    	
    	String dataStr = new String(databytes, 0, databytes.length);
    	
    	System.out.println(dataStr);
    	
    	
    	String[] dataArr = dataStr.split(":");
    	String head =  dataArr[0];
    	String[] headArr = head.split(" ");
    	
    	//determine the request message
    	if(headArr[0].equals("REG")){
    		if(headArr[1].equals("REPLY")){
    			// waiting
    			try{
    				Thread.sleep(1000);
    			}catch(Exception e){}
    			this.onRegisterReply(pkg);
    			Thread.interrupted();
    		}
    		if(headArr[1].equals("DONE")){
    			this.onRegisterDone(pkg);
    		}
    	}
    	*/
	}
	
	
	/*
	public void onRegisterReply(FooPacket pkg){
		
		
		String ipAddr = pkg.getDatagramPacket().getAddress().getHostAddress();
		byte[] pkgBytes = pkg.getData();
		String pkgStr = new String(pkgBytes, 0, pkgBytes.length);
		//System.out.println(pkgStr);
		
    	
    	String[] dataArr = pkgStr.split(":");
    	//String head = dataArr[0];
    	String body = dataArr[1];
    	
    	//System.out.println("HEAD "+head);
    	//if(!head.equals("REG REPLY")){
    		
    	//	return;
    	//}
    	
    	String[] bodyArr = body.split(",");
    	int seq = Integer.parseInt(bodyArr[0]);
    	String devPubIDStr = bodyArr[1];
    	
    	// get byte array data
    	byte[] devPubID = SecureControlPoint.hexStringToBytes(devPubIDStr);
    	String RND = bodyArr[2];
    	String id = bodyArr[3];
    	
    	

    	// Show recieved
    	System.out.println("ID "+id);
    	System.out.println("SEQ# "+seq);
    	System.out.println("Random number "+RND);
    	//System.out.println("Public ID "+devPubIDStr);
    	
    	//if(!id.equals(this.ID) || seq != this.currentSeq){
    	//	return;
    	//}
    	
    	//3.2 compute secret key
    	this.readKeyFile();
    	this.computeSecret(devPubID);
    	
    	//show secret computed
    	String secretStr = new String(this.secret, 0, this.secret.length);
    	System.out.println("Secret "+secretStr);
    	
    	
    	//3.3 Hash RND+secret
    	byte[] RNDBytes = RND.getBytes();
    	byte[] tmpBytesToHash = new byte[RNDBytes.length+this.secret.length];
    	System.arraycopy(RNDBytes, 0, tmpBytesToHash, 0, RNDBytes.length);
    	System.arraycopy(this.secret, 0, tmpBytesToHash, RNDBytes.length, this.secret.length);
    	
    	//digest using md5 algorithm
    	byte[] messageDigest = null;
    	try{
    		MessageDigest algorithm = MessageDigest.getInstance("MD5");
    		algorithm.reset();
    		algorithm.update(tmpBytesToHash);
    		messageDigest = algorithm.digest();
    		
    		StringBuffer hexString = new StringBuffer();
    		for(int i=0; i<messageDigest.length; i++){
    			hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
    		}
    		System.out.println("Message Digested "+hexString);
    	}catch(NoSuchAlgorithmException e){} 
    	String messageDigestStr = SecureControlPoint.toHexString(messageDigest);
    	
    	
    	//3.4 prepare reply message
    	String requestStr = "REG CONFIRM:";
    	requestStr += seq+",";

    	
    	//make 2 dimension arrays into one long array
    	byte[] tmpPubID = new byte[(l+1)*keylen];
    	int current = 0;
    	for(int i=0; i<l+1; i++){
    		System.arraycopy(this.publicIDBytes[i], 0, tmpPubID, current, keylen);
    		current += keylen;
    	}
    	String tmpPubIDStr = SecureControlPoint.toHexString(tmpPubID);
    	
    	requestStr += tmpPubIDStr+",";              
    	requestStr += messageDigestStr+",";
    	requestStr += id+",";
    	requestStr += getPort();
    	
    	sendRegistRequest(requestStr, ipAddr, 4555);
    	
    	this.currentConfirmStr = requestStr;
    	// show reply message
    	//System.out.println(requestStr);
    	
    	
		
		this.secureGroupStatus = SecureControlPoint.CONFIRMING;
    	
	}
	*/
	/*
	private void sendRegistRequest(String requestStr, String ipAddr, int port){
		FooResponse resp = new FooResponse();
		resp.setData(requestStr.getBytes());
		
		FooUSocket sock = getFooUSocket();
		sock.post(ipAddr, port, resp);
	}
	*/
	/*
	public void onRegisterDone(FooPacket pkg){
		//
		
		byte[] databytes = pkg.getData();
    	String dataStr = new String(databytes, 0, databytes.length);
    	String[] dataArr = dataStr.split(":");
    	String head = dataArr[0];
    	String body =  dataArr[1];
    	String[] bodyArr = body.split(",");
    	
    	//String ipAddr = pkg.getDatagramPacket().getAddress().getHostAddress();
    	
    	//int seq = Integer.parseInt(bodyArr[0]);
    	
    	//compute group key
    	String groupKeyStr = bodyArr[1];
    	byte[] xorResult = SecureControlPoint.hexStringToBytes(groupKeyStr);
    	this.groupKey = SecureControlPoint.bitwiseXOR(this.secret, xorResult);
    	
    	//String id = bodyArr[2];
    	
    	//System.out.println(dataStr);
    	
    	if(head.equals("REG DONE")){
    		this.secureGroupStatus = SecureControlPoint.REGISTERED;
    		System.out.println(getName()+" REGISTERED");
    	}
		
		
		
	}
	*/
	
	
	
	private void testSecureGroup(){
		FooUSocket uSock = new FooUSocket();
		uSock.open(4566);
		FooPacket pkg = uSock.receive();
		uSock.close();
		byte[] dataBytes = pkg.getData();
		byte[] clearBytes = null;
		
		try{
			Cipher dec = Cipher.getInstance("DES");
			
			DESKeySpec keyspec = new DESKeySpec(this.groupKey);
			SecretKeyFactory keyfact = SecretKeyFactory.getInstance("DES");
			SecretKey secret = keyfact.generateSecret(keyspec);
			
			dec.init(Cipher.DECRYPT_MODE, secret);
			
			clearBytes = dec.doFinal(dataBytes);
			
			String clear = new String (clearBytes, 0 , clearBytes.length);
			System.out.println("Clear string="+clear);
			
		}catch(Exception e){
			e.printStackTrace();
		}
	}
	
	/*
	public void secureRegister(){
		// get time
		
		String ipAddr = searchDevice(); //until device found
		if(ipAddr == null){
			System.out.println("Device is not support secure group");
			return;
		}else{
			System.out.println("Secure Aircon found");
		}
		regist(ipAddr);
		// waiting util time out
		try{
			Thread.sleep(5000);
		}catch(Exception e){
			e.printStackTrace();
		}
		
		
		String status = getStatus();
		if(status.equals(SecureControlPoint.REGISTERED)){
			return;
		}
		
		while(!status.equals(SecureControlPoint.CONFIRMING)){
			String requestStr = getRequestStr();
			sendRegistRequest(requestStr, ipAddr, 4555);
			// waiting
			try{
				Thread.sleep(1000);
			}catch(Exception e){
				e.printStackTrace();
			}
			
			status = getStatus();
		}
		
		if(status.equals(SecureControlPoint.REGISTERED)){
			return;
		}
		
		// waiting util time out
		try{
			Thread.sleep(5000);
		}catch(Exception e){
			e.printStackTrace();
		}
		
		while(!status.equals(SecureControlPoint.REGISTERED)){
			
			String confirmStr = getConfirmStr();
			sendRegistRequest(confirmStr, ipAddr, 4555);
			
			// waiting
			try{
				Thread.sleep(1000);
			}catch(Exception e){
				e.printStackTrace();
			}
			
			status = getStatus();
		}
		
		
	
	}
	*/
	
	private String searchDevice(){
		//check if secure device is discovered
		Device dev = getDevice(AIRCON_DEVICE_TYPE);
		while(dev == null){
			//System.out.println("SEARCHING ...");
			//stop();
			//start();
			search();
			try{
				Thread.sleep(700);
			}catch(Exception e){}

			dev = getDevice(AIRCON_DEVICE_TYPE);
			
		}
		
		
		
		//check if it is support secure group communication
		Action isSupportSecureGroup = dev.getAction("IsSecured");

		if(isSupportSecureGroup.postControlAction() == false){ //connect to device
			return null;
		}
		
		ArgumentList outArgList = isSupportSecureGroup.getOutputArgumentList();
		String securegroupsupportState = outArgList.getArgument(0).getValue();
		if(!securegroupsupportState.equals("1")){
			return null;
		}
		
		//get device's details
		String location = dev.getLocation();
		String friendlyName = dev.getFriendlyName();
		int httpPort = dev.getHTTPPort();
		String interfaceAddress = dev.getInterfaceAddress();
		
		//ssdp within device
		SSDPPacket ssdpPkg = dev.getSSDPPacket();
		String localAddress = ssdpPkg.getLocalAddress();
		String server = ssdpPkg.getServer();
		
		/*
		System.out.println("Device details:");
		System.out.println("\tLocation: "+location);
		System.out.println("\tFriendly name: "+friendlyName);
		System.out.println("\tHTTP Port: "+httpPort);
		System.out.println("\tInterface addr: "+interfaceAddress);
		System.out.println("\tLocal addr: "+localAddress);
		System.out.println("\tServer: "+server);
		*/
		return interfaceAddress;
	}
	
	public void regist(){
		
		String ipAddr = searchDevice();
		//String ipAddr = "172.30.129.31";
		
		long startTime = System.currentTimeMillis();
		
		//Steps in Registration
		//1. Control Point send    REG REQUEST:::#SEQ,,, ID
		//2. ---Device send        REG REPLY::: #SEQ,,, PubID(dev),,, RND,,, ID
		//3. Control Point 
		//   3.1 Compute secret key = PubID(dev) x CP's secret
		//   3.2 Send              REG CONFIRM:::#SEQ,,, PubID(cp),,, H(RND, secret),,, ID ,, port
		//4. ---Device
		//   4.1 Compute secret key = PubID(cp) x DEV's secret
		//   4.2 Check H(RND, secret) <-- authentication
		//   4.3 Update new group key
		//   4.4 Update to exist member (optional)
		//   4.5 bit wise xor group key with secret key
		//   4.6 send 			   REG DONE:::#SEQ,,, xor result,,, ID
		//5. Control Point
		//   5.1 Compute group key = xor result xor secret key
		//   5.2 Update group key   
		
		//1.Control Point send REG REQUEST
		//1.1 create REG REQUEST MESSAGE
		String requestStr = "REG REQUEST:";
		//String id = this.ID;
		
		//  random connection sequence number
		Random rand = new Random();
		int seq = rand.nextInt(); 
		if(seq<0){
			seq *= -1;
		}
		this.currentSeq = seq;
		
		requestStr += seq+",";
		requestStr += getName();
		
		// REG REQUEST
		//System.out.println(requestStr);
		

		//sendRegistRequest(requestStr, ipAddr, 4555);
		
		this.secureGroupStatus = SecureControlPoint.REQUESTING;
		
		this.currentRequestStr = requestStr;
		
		// Networking
		//1.2 Now register request ready to send to secure device(server)
		FooResponse response = new FooResponse();
		response.setData(requestStr.getBytes());
		FooUSocket uSock = null;
		//int currentPort = 6000;
		//while(uSock.open(currentPort) == false){
		//	currentPort++;
			
		//}
		FooPacket pkg = null;
		
		while(pkg == null){
			uSock = new FooUSocket();
			uSock.open();
			uSock.post(ipAddr, 4555, response);
			//uSock.close();
			
			
			//2. Device Move to device turn
			
			
			
			//3. Control Point listening
			//uSock = new FooUSocket();
			//uSock.open(remotePort);
			
			pkg = uSock.receive();
			
			uSock.close();
			
			//if(pkg == null)
				//System.out.println("Request timeout id="+this.ID);
				
			
		}
		/*
		while(pkg == null){
			System.out.println("Request time out");
			uSock.post(interfaceAddress, 4555, response); //retransmit
			pkg = uSock.receive();
			//return;
			try{
				Thread.sleep(1500);
			}catch(Exception e){}
		}*/
		//uSock.close();
		
		//try{
		//	Thread.sleep(500);
		//}catch(InterruptedException e){}
		
		
		//3.1 extract information replied
		ipAddr = pkg.getDatagramPacket().getAddress().getHostAddress();
		byte[] pkgBytes = pkg.getData();
		String pkgStr = new String(pkgBytes, 0, pkgBytes.length);
		//System.out.println(pkgStr);
		
    	
    	String[] dataArr = pkgStr.split(":");
    	String head = dataArr[0];
    	String body = dataArr[1];
    	
    	//System.out.println("HEAD "+head);
    	//if(!head.equals("REG REPLY")){
    		
    	//	return;
    	//}
    	
    	String[] bodyArr = body.split(",");
    	seq = Integer.parseInt(bodyArr[0]);
    	String devPubIDStr = bodyArr[1];
    	
    	// get byte array data
    	byte[] devPubID = SecureControlPoint.hexStringToBytes(devPubIDStr);
    	String RND = bodyArr[2];
    	String id = bodyArr[3];
    	
    	

    	// Show what device replied
    	//System.out.println("ID "+id);
    	//System.out.println("SEQ# "+seq);
    	//System.out.println("Random number "+RND+"\n\n");
    	//System.out.println("Public ID "+devPubIDStr);
    	
    	//if(!id.equals(this.ID) || seq != this.currentSeq){
    	//	return;
    	//}
    	
    	//3.2 compute secret key
    	this.readKeyFile();
    	//long startTime = System.nanoTime();
    	this.computeSecret(devPubID);
    	//long stopTime = System.nanoTime();
    	
    	//long computeSecretTime = stopTime - startTime;
    	
    	
    	//show secret computed
    	String secretStr = new String(this.secret, 0, this.secret.length);
    	//System.out.println("Secret "+secretStr);
    	
    	
    	//3.3 Hash RND+secret
    	byte[] RNDBytes = RND.getBytes();
    	
    	String messageDigestStr = this.hashWithSecret(RNDBytes);
    	
    	
    	//3.4 prepare reply message
    	requestStr = "REG CONFIRM:";
    	requestStr += seq+",";

    	
    	//make 2 dimension arrays into one long array
    	String tmpPubIDStr = this.getEncodedPublicID();
    	
    	requestStr += tmpPubIDStr+",";              
    	requestStr += messageDigestStr+",";
    	requestStr += id+",";
    	requestStr += this.groupUSock.getPort();
    	
    	// show reply message
    	//System.out.println(requestStr);
	
    	//3.5 networking
    	//try{
    	//	Thread.sleep(500);
    	//}catch(InterruptedException e){}
    	
    	FooResponse replyResponse = new FooResponse();
    	replyResponse.setData(requestStr.getBytes());
    	
    	
    	
    	//currentPort = 6001;
		//while(uSock.open(currentPort) == false){
		//	currentPort++;
			
		//}
    	
    	
    	pkg = null;
    	while(pkg == null){
    		uSock = new FooUSocket();
    		uSock.open();
	    	uSock.post(ipAddr, 4555, replyResponse);
	    	//uSocket.close();
	    	
	    	//Now let's turn to device
	    	
	    	//4. Reg done
	    	//uSock = new FooUSocket();
			//uSock.open(remotePort);
			pkg = uSock.receive();
			uSock.close();
			
			//if(pkg == null)
				//System.out.println("Confirm timeout id="+this.ID);
    	}
		/*
		while(pkg == null){
			System.out.println("Request time out");
			uSock.post(ipAddr, 4555, replyResponse); //retransmit
			pkg = uSock.receive();
			//return;
			try{
				Thread.sleep(1500);
			}catch(Exception e){}
		}*/
		
		
		//try{
		//	Thread.sleep(500);
		//}catch(InterruptedException e){}
    	
		byte[] databytes = pkg.getData();
    	String dataStr = new String(databytes, 0, databytes.length);
    	dataArr = dataStr.split(":");
    	head = dataArr[0];
    	body =  dataArr[1];
    	bodyArr = body.split(",");
    	
    	ipAddr = pkg.getDatagramPacket().getAddress().getHostAddress();
    	
    	seq = Integer.parseInt(bodyArr[0]);
    	
    	//compute group key
    	String groupKeyStr = bodyArr[1];
    	byte[] xorResult = SecureControlPoint.hexStringToBytes(groupKeyStr);
    	this.groupKey = SecureControlPoint.bitwiseXOR(this.secret, xorResult);
    	
    	id = bodyArr[2];
    	String groupkeyID = bodyArr[3];
    	
    	//System.out.println(dataStr);
    	
    	if(head.equals("REG DONE")){
    		setGroupkeyID(groupkeyID);
    		this.secureGroupStatus = SecureControlPoint.REGISTERED;
    		
    		long finishTime = System.currentTimeMillis();
    		long espTime = finishTime - startTime;
    		System.out.println(id+"\t"+espTime);
    		
    		//System.out.println("+++ +++ +++ Node id "+this.ID+" registered groupkeyID="+getGroupkeyID());
    		
    		if(this.enableLogFile){
	    		String log = "+++ +++ +++ Node id "+this.ID+" registered groupkeyID="+getGroupkeyID()+"\n";
	    		try{
	    			this.out.write(log.getBytes());
	    		}catch(Exception e){}
    		}
    	}
    	
    	startSecureGroup();
    	/*
    	System.out.println("Time to compute secret "+computeSecretTime);
    	try{
	    	FileOutputStream fos = new FileOutputStream("secret_com.txt",true);
	    	String timeStr = Long.toString(computeSecretTime);
	    	timeStr += "\n\r";
	    	fos.write(timeStr.getBytes());
	    	fos.close();
	    	
    	}catch(Exception e){
    		e.printStackTrace();
    	}
    	*/
    	//try{
    	//	Thread.sleep(1500);
    	//}catch(Exception e){}
    	

	}
	
	private boolean readKeyFile(){
    	// see full verion at org.geed.kpd.test.TestKey
    	
		
		try{
			// Read key bytes (8)
			FileInputStream in = new FileInputStream("secret\\m"+getName()+".txt");
			
		
			this.secretBytes = new byte[l+1][keylen];
			this.publicIDBytes = new byte[l+1][keylen];
			

			
			for(int i=0; i<l+1; i++){
				in.read(this.secretBytes[i], 0, this.secretBytes[i].length);
				//in.read(publicG1[i], 0, publicG1[i].length);
				//System.out.print(this.secretBytes[i]+" ");
						
			}
			//System.out.println();
			
			for(int i=0; i<l+1; i++){
				//in.read(secretA1[i], 0, secretA1[i].length);
				in.read(this.publicIDBytes[i], 0, this.publicIDBytes[i].length);
				//System.out.print(this.publicIDBytes[i]+" ");
			}
			
			
			
			
			return true;
			
		}catch(IOException ioe){
			ioe.printStackTrace();
		}
		
		return false;
		
		
    }
	
	private void computeSecret(byte[] publicIDBytes){
		// see full version at org.geed.kpd.test.TestKey
		
		byte[][] publicID = new byte[l+1][keylen];
		int current =0;
		for(int i=0; i<l+1; i++){
			System.arraycopy(publicIDBytes, current, publicID[i], 0, keylen);
			current += keylen;
		}
		
		
		
		byte[] result = new byte[keylen];
		for(int i=0; i<l+1; i++){
			byte[] tmp = new byte[keylen];
			
			for(int j=0; j<keylen; j++){
				tmp[j] = (byte)(this.secretBytes[i][j]*publicID[i][j]);
			}
			
			for(int j=0; j<keylen; j++){
				result[j] += tmp[j];
			}
			
			
		}
		
		//set secret bytes use to generate secret key
		this.secret = result;
	}

	public void eventNotifyReceived(String uuid, long seq, String name, String value)
	{
	}
	
	public void deviceSearchResponseReceived(SSDPPacket packet)
	{
		
	}
	
	public void deviceNotifyReceived(SSDPPacket packet)
	{
		//System.out.println("INFO:Device notify receive");
	}
	


	private String hashWithSecret(byte[] RNDBytes){
		
    	byte[] tmpBytesToHash = new byte[RNDBytes.length+this.secret.length];
    	System.arraycopy(RNDBytes, 0, tmpBytesToHash, 0, RNDBytes.length);
    	System.arraycopy(this.secret, 0, tmpBytesToHash, RNDBytes.length, this.secret.length);
    	
    	//digest using md5 algorithm
    	byte[] messageDigest = null;
    	try{
    		MessageDigest algorithm = MessageDigest.getInstance("MD5");
    		algorithm.reset();
    		algorithm.update(tmpBytesToHash);
    		messageDigest = algorithm.digest();
    		
    		StringBuffer hexString = new StringBuffer();
    		for(int i=0; i<messageDigest.length; i++){
    			hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
    		}
    		//System.out.println("Message Digested "+hexString);
    	}catch(NoSuchAlgorithmException e){
    		System.out.println("Message Digest Error");
    	} 
    	
    	return SecureControlPoint.toHexString(messageDigest);
    	
	}
	
    public static byte[] bitwiseXOR(byte[] b1, byte[] b2){
    	if(b1.length != b2.length)
    		return null;
    	
    	byte[] tmp = new byte[b1.length];
    	
    	for(int i=0; i<tmp.length; i++){
    		tmp[i] = (byte)(b1[i] ^ b2[i]);
    	}
    	
    	return tmp;
    }
	
    /*
     * Converts a byte to hex digit and writes to the supplied buffer
     */
    private static void byte2hex(byte b, StringBuffer buf) {
        char[] hexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
                            '9', 'A', 'B', 'C', 'D', 'E', 'F' };
        int high = ((b & 0xf0) >> 4);
        int low = (b & 0x0f);
        buf.append(hexChars[high]);
        buf.append(hexChars[low]);
    }
    
    private static byte[] hexStringToBytes( String s ) {
    	/* Thanks Mask_s http://forums.sun.com/thread.jspa?threadID=5129024 */

    	int iLength = s.length();
    	int iBuff = iLength / 2;

    	byte[] buff = new byte[ iBuff ];

    	int j = 0;
    	for(int i = 0; i < iLength; i+=2) {

	    	try {
	
	    		String s1 = s.substring(i, i+2);
	    		buff[j++] = (byte) Integer.parseInt(s1, 16);
	
	    	} catch ( Exception e ) {
	    		e.printStackTrace();
	    	}

    	}

    	return buff;

    }

    /*
     * Converts a byte array to hex string
     */
    private static String toHexString(byte[] block) {
        StringBuffer buf = new StringBuffer();
       
        int len = block.length;

        // int j=0;
        for (int i = 0; i < len; i++) {
             //j++;
        	 byte2hex(block[i], buf);
             //if (i < len-1) {
             //    buf.append(":");
             //}
            // if (j>=25){
            ///	 buf.append("\n\t\t");
            //	 j=0;
             //}
        }
        return buf.toString();
    }

	private byte[] getGroupKey() {
		return groupKey;
	}

	private void setGroupKey(byte[] groupKey) {
		this.groupKey = groupKey;
	}

	private String getGroupkeyID() {
		return groupkeyID;
	}

	private void setGroupkeyID(String groupkeyID) {
		this.groupkeyID = groupkeyID;
	}
	

}
